package mms.farina.thecell;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Calendar;

import mms.farina.thecell.activities.MainActivity;
import mms.farina.thecell.messages.CustomMessage;
import mms.farina.thecell.messages.MySerialization;
import mms.farina.thecell.messages.ResendPoolItems;
import mms.farina.thecell.services.ReceiveService;
import mms.farina.thecell.support.CryptoFunction;
import mms.farina.thecell.support.ErrorCodes;
import android.os.Message;

/**Classe usata per l'invio dei messaggi*/
public class Sender2 {

	private DatagramSocket socket = null;
	private int toPort=ReceiveService.PORT_NEIGHBOR;
	private String toIp=ReceiveService.IP_NEIGHBOR;
	private CustomMessage tmpMessage;


	public Sender2(){
		try {
			socket=new DatagramSocket();
		} catch (SocketException e) {
			sendError(ErrorCodes.SOCKET_OPENING_ERROR_MSG, ErrorCodes.SOCKET_OPENING_ERROR);
			e.printStackTrace();
		}
	}

	/**Invia un messaggio attraverso un nuovo thread*/
	private Runnable mTask = new Runnable() {
		public void run() {
			sendToSocket(tmpMessage);
		}
	};


	public void updateNetValues(String toIp, int toPort){
		this.toIp=toIp;
		this.toPort=toPort;
	}

	public void sendToSocket(CustomMessage message) {
		
		if(!message.getType().equals(CustomMessage.MESSAGE_TYPE_MEX)){
			toIp=ReceiveService.IP_NEIGHBOR;
			toPort=ReceiveService.PORT_NEIGHBOR;
		}

		if(toIp.isEmpty())return;
		byte[] buf;
		DatagramPacket packet;

		buf = new byte[4096];
		packet = new DatagramPacket(buf, buf.length);
		try {
			buf=MySerialization.SerializeMessage(message);
			packet = new DatagramPacket(buf, buf.length, InetAddress.getByName(toIp), toPort);
			socket.send(packet);
			Message m=new Message();
			m.obj=message.getType();
			m.what=1;
			if(ReceiveService.verboseNotification)MainActivity.recvHandler.sendMessage(m);

		} catch (SocketException e) {
			sendError(ErrorCodes.SOCKET_OPENING_ERROR_MSG, ErrorCodes.SOCKET_OPENING_ERROR);
			e.printStackTrace();
		} catch (IOException e) {
			sendError(ErrorCodes.GENERIC_ERROR_MSG, ErrorCodes.GENERIC_ERROR);
			e.printStackTrace();
		}
		if(message.getType().equals(CustomMessage.MESSAGE_TYPE_MEX)) addToResendPool(message);

	}

	/**Invia un messaggio attraverso un nuovo thread*/
	private void sendToThreadedSocket(CustomMessage message){
		tmpMessage=message;
		Thread senderThread = new Thread(mTask, Calendar.getInstance().getTimeInMillis()+"");
		senderThread.run();

	}

	public void closeSender(){
		socket.close();
	}



	/**Invia un messaggio unicast*/
	public void sendMessageAlias(String alias, CustomMessage message, boolean ta){
		String[] s=ReceiveService.netMembers.get(alias);
		if(s==null) return;
		toIp=s[0];
		toPort=Integer.parseInt(s[1]);

		if(message.getType().equals(CustomMessage.MESSAGE_TYPE_MEX)){
			String msgdecr="";
			try {
				msgdecr=CryptoFunction.AES_Decrypt(message.getTextMessage(), CryptoFunction.MD5(ReceiveService.PASSWORD));
			} catch (Exception e) {
				sendError(ErrorCodes.GENERIC_ERROR_MSG, ErrorCodes.GENERIC_ERROR);
				e.printStackTrace();
			}
			String tmp=ReceiveService.NAME+">"+msgdecr+"\n";
			if(ReceiveService.convStore.get(alias)!=null)tmp=ReceiveService.NAME+">"+msgdecr+"\n"+ReceiveService.convStore.get(alias);
			ReceiveService.convStore.put(alias, tmp);
			
		}
		if(ta) sendToThreadedSocket(message);
		else sendToSocket(message);
	}

	/**Invia un messaggio broadcast o di servizio*/
	public void sendMessage(CustomMessage message, boolean ta){

		if(message.getType().equals(CustomMessage.MESSAGE_TYPE_MEX)){
			String msgdecr="";
			try {
				msgdecr=CryptoFunction.AES_Decrypt(message.getTextMessage(), CryptoFunction.MD5(ReceiveService.PASSWORD));
			} catch (Exception e) {
				sendError(ErrorCodes.GENERIC_ERROR_MSG, ErrorCodes.GENERIC_ERROR);
				e.printStackTrace();
			}
			String tmp=ReceiveService.NAME+">"+msgdecr+"\n";
			if(ReceiveService.convStore.get("BROADCAST")!=null) tmp=ReceiveService.NAME+">"+msgdecr+"\n"+ReceiveService.convStore.get("BROADCAST");
			ReceiveService.convStore.put("BROADCAST", tmp);

		}

		if(ta) sendToThreadedSocket(message);
		else sendToSocket(message);


	}

	private void addToResendPool(CustomMessage message){
		try {
			ReceiveService.poolLock.lock();
		} catch (InterruptedException e) {
			sendError(ErrorCodes.GENERIC_ERROR_MSG, ErrorCodes.GENERIC_ERROR);
			e.printStackTrace();
		}
			try {
				String hash=CryptoFunction.MD5(MySerialization.SerializeMessage(message));

				if(ReceiveService.resendPool.containsKey(hash)){
					ArrayList<ResendPoolItems> tmp=ReceiveService.resendPool.get(hash);
					tmp.add(new ResendPoolItems(message, Calendar.getInstance()));
					ReceiveService.resendPool.put(hash, tmp);
				}
				else {
					ArrayList<ResendPoolItems> tmp=new ArrayList<ResendPoolItems>();
					tmp.add(new ResendPoolItems(message, Calendar.getInstance()));
					ReceiveService.resendPool.put(hash, tmp);
				}
			} catch (NoSuchAlgorithmException e) {
				sendError(ErrorCodes.GENERIC_ERROR_MSG, ErrorCodes.GENERIC_ERROR);
				e.printStackTrace();
			} catch (UnsupportedEncodingException e) {
				sendError(ErrorCodes.GENERIC_ERROR_MSG, ErrorCodes.GENERIC_ERROR);
				e.printStackTrace();
			}
		ReceiveService.poolLock.unlock();
	}


	private void sendError(String message, int code){
		Message m=new Message();
		m.obj=message;
		m.what=code;
		MainActivity.regProgressHandler.sendMessage(m);
	}

}
